package main

import (
	"UpgradeServer/Common"
	Grpcserver "UpgradeServer/GrpcServer"
	"UpgradeServer/UpgradeGrpc"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"syscall"
	"time"

	"github.com/sevlyar/go-daemon"
	"google.golang.org/grpc"
)

type Configuration struct {
	ServerPort   int
	MySqlAddress string
	MySqlPort    int
	MysqlUser    string
	MysqlPass    string
	LogDir       string
	FileDir      string
}

func init() {
	Common.MySqlAddress = ""
	Common.MySqlPort = 0
	Common.MysqlUser = ""
	Common.MysqlPass = "1234"
	Common.LogDir = "./logs"
	Common.FileDir = ""
}

var g_server Server = Server{}

// program enter
func main() {
	// create log file
	logFile := CreateLogFile()
	if logFile == nil {
		fmt.Println("Error: ", "create log file error")
		return
	}
	defer logFile.Close()

	bret := ReadConfig()
	if !bret {
		fmt.Println("Error: ", "read config.json error")
		log.Println("Error: ", "read config.json error")
		return
	}
	// create logdir and filedir
	log.Println("create logdir and filedir")
	CreateFileDir()

	// setup signal
	log.Println("setup signal")
	SetupSignal()

	// daemonize
	ctx := daemon.Context{}
	if runtime.GOOS == "linux" {
		ctx = daemon.Context{
			PidFileName: "/var/tmp/upserver.pid",
			PidFilePerm: 0644,
			LogFileName: "/var/tmp/upserver.log",
			LogFilePerm: 0640,
			WorkDir:     "./",
			Umask:       027,
			Args:        []string{"[UPServer]"},
		}
		d, err := ctx.Reborn()
		if err != nil {
			log.Println("Error: ", err)
			return
		}
		if d != nil {
			return
		}
	}
	defer ctx.Release()

	// start grpc server
	log.Println("start grpc server")
	g_server.StarGrpcServer()
	log.Print("grpc server stop")
}

func CreateLogFile() *os.File {
	_, err := os.Stat(Common.LogDir)
	if os.IsNotExist(err) {
		err := os.Mkdir(Common.LogDir, 0755)
		if err != nil {
			fmt.Println("Create log dir error:", err)
			return nil
		}
	}
	var logFile *os.File
	filePath := Common.LogDir + "/" + time.Now().Local().Format(time.DateOnly) + ".log"
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		logFile, err = os.Create(filePath)
		if err != nil {
			fmt.Println("Error: ", err)
			return nil
		}

	} else {
		logFile, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			fmt.Println("Error: ", err)
			return nil
		}
	}
	log.SetOutput(logFile)
	return logFile
}

// / read config.json
func ReadConfig() bool {
	file, _ := os.Open("config.json")
	defer file.Close()

	// parse config.json
	decoder := json.NewDecoder(file)
	conf := Configuration{}
	err := decoder.Decode(&conf)
	if err != nil {
		fmt.Println("Error: ", "Parse config.json error")
		log.Println("Error: ", "Parse config.json error")
		return false
	}

	if conf.ServerPort <= 1024 {
		fmt.Println("Error: ", "read server port error")
		log.Println("Error: ", "read server port error")
		return false
	}
	Common.ServerPort = conf.ServerPort

	// get mysql address and port
	if conf.MySqlAddress == "" {
		fmt.Println("Error: ", err)
		log.Println("Error: ", "read mysql address error")
		return false
	}
	Common.MySqlAddress = conf.MySqlAddress

	if conf.MySqlPort <= 0 {
		fmt.Println("Error: ", "read mysql address error")
		log.Println("Error: ", "read mysql port error")
		return false
	}
	Common.MySqlPort = conf.MySqlPort

	// get mysql user and pass
	if conf.MysqlUser == "" {
		fmt.Println("Error: ", "read mysql user error")
		log.Println("Error: ", "read mysql user error")
		return false
	}
	Common.MysqlUser = conf.MysqlUser

	if conf.MysqlPass != "" {
		Common.MysqlPass = conf.MysqlPass
	}

	// get log dir
	if conf.LogDir == "" {
		Common.LogDir = "./logs"
	}
	Common.LogDir = conf.LogDir

	// get file dir
	if conf.FileDir == "" {
		Common.FileDir = "./files"
		return false
	}
	Common.FileDir = conf.FileDir

	return true
}

func CreateFileDir() {
	_, err := os.Stat(Common.FileDir)
	if os.IsNotExist(err) {
		err := os.Mkdir(Common.FileDir, 0755)
		if err != nil {
			fmt.Println("Error: ", err)
			log.Println("Error: ", err)
		}
	}
}

func SetupSignal() {
	sig := make(chan os.Signal, 4)
	signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	go func() {
		for {
			s := <-sig
			switch s {
			case syscall.SIGHUP:
				fmt.Println("Receive signal SIGHUP")
				log.Println("Receive signal SIGHUP")
			case syscall.SIGINT:
				fmt.Println("Receive signal SIGINT")
				log.Println("Receive signal SIGINT")
			case syscall.SIGTERM:
				fmt.Println("Receive signal SIGTERM")
				log.Println("Receive signal SIGTERM")
			case syscall.SIGQUIT:
				fmt.Println("SIGQUIT signal received, exiting...")
				log.Printf("SIGQUIT signal received, exiting...")
				g_server.StopServer()
				os.Exit(0)
			}
		}
	}()
}

type Server struct {
	UpgradeGrpc.UnimplementedUpgradeServiceServer
	grpcServer    *grpc.Server
	upgradeServer *Grpcserver.UpgradeServiceServer
}

// start grpc server
func (s *Server) StarGrpcServer() {
	var serverOption = []grpc.ServerOption{
		grpc.MaxRecvMsgSize(math.MaxInt32),
		grpc.MaxSendMsgSize(math.MaxInt32),
	}

	listen, err := net.Listen("tcp", ":"+strconv.Itoa(Common.ServerPort))
	if err != nil {
		log.Println("Failed net.Listen : ", err)
		return
	}
	log.Println("listen success on port: ", Common.ServerPort)

	s.grpcServer = grpc.NewServer(serverOption...)
	s.upgradeServer = Grpcserver.NewUpgradeServiceServer()
	UpgradeGrpc.RegisterUpgradeServiceServer(s.grpcServer, s.upgradeServer)
	s.grpcServer.Serve(listen)
	//defer s.grpcServer.GracefulStop()
	//defer s.upgradeServer.Close()
}

// stop server
func (s *Server) StopServer() {
	s.grpcServer.GracefulStop()
	s.upgradeServer.Close()
}
